﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;



using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
using DotSpatial.Controls;
using DotSpatial.Data;

using DotSpatial.Topology;

using DotSpatial.Symbology;

using Point = System.Drawing.Point;



namespace HAGISV2
{
    public class HAddLineMF:HMapFunction
    {
        #region private variables

        //private IFeatureSet _featureSet;
        private List<Coordinate> _coordinates;
       
        private Point _mousePosition;
       
        private IMapLineLayer _tempLayer;
      
        private List<List<Coordinate>> _parts;
        private bool _disposed;

        /// <summary>
        /// 线顶点Id最多两个，画完线时自动清空，
        /// 若顶点已有相同，则string为相同顶点的Id。
        /// 若无相同则添加""空字符串，占位。
        /// </summary>
        private List<string> _lineVertexIds = new List<string>();
        /// <summary>
        /// 线上（非点）捕捉使用
        /// </summary>
        Dictionary<List<int>, Coordinate> _insertCoordWithIndex = new Dictionary<List<int>, Coordinate>();
        /// <summary>
        /// 线上（非点）捕捉 两点处于同一线段间时排序使用 
        /// </summary>
        List<double> _distance = new List<double>();

        //付加

        Coordinate _currentCoords=new Coordinate ();
        MFSHAPETYPE _mfShapeType;

       

        FeatureType _featureType;

        #endregion

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the AddShapeFunction class.  This specifies the Map that this function should be applied to.
        /// </summary>
        /// <param name="map">The map control that implements the IMap interface that this function uses.</param>
        public HAddLineMF(IMap map)
            : base(map)
        {
            Configure();
        }

        private void Configure()
        {
            YieldStyle = (YieldStyles.LeftButton | YieldStyles.RightButton);
           
            _parts = new List<List<Coordinate>>();
        }

        #endregion

        #region Methods

        /// <summary>
        /// Forces this function to begin collecting points for building a new shape.
        /// </summary>
        protected override void OnActivate()
        {
            if (_standBy == false) { _coordinates = new List<Coordinate>(); }
            if (_tempLayer != null)
            {
                Map.MapFrame.DrawingLayers.Remove(_tempLayer);
                Map.MapFrame.Invalidate();
                Map.Invalidate();
                _tempLayer = null;
            }
            _standBy = false;
            base.OnActivate();
        }

        /// <summary>
        /// Allows for new behavior during deactivation.
        /// </summary>
        protected override void OnDeactivate()
        {
            base.OnDeactivate();


            //付加，暂时不允许其他额外的操作
            this._coordinates = new List<Coordinate>();

        }

        /// <summary>
        /// Handles drawing of editing features.
        /// </summary>
        /// <param name="e">The drawing args for the draw method.</param>
        protected override void OnDraw(MapDrawArgs e)
        {
            if (!(this.InteractiveSystem.CurrentLayer is VectorLayer) || !this.InteractiveSystem.CurrentLayer.Visible) return;
            if (_standBy) { return; }
            if (_featureType == FeatureType.Point) { return; }

            e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;

            this.DisplayFeature(e);

            // Draw any completed parts first so that they are behind my active drawing content.
            if (_parts != null)
            {
                GraphicsPath gp = new GraphicsPath();

                List<Point> partPoints = new List<Point>();
                foreach (List<Coordinate> part in _parts)
                {
                    partPoints.AddRange(part.Select(c => Map.ProjToPixel(c)));
                    if (_featureType == FeatureType.Line)
                    {
                        gp.AddLines(partPoints.ToArray());
                    }
                    if (_featureType == FeatureType.Polygon)
                    {
                        gp.AddPolygon(partPoints.ToArray());
                    }
                    partPoints.Clear();
                }
                e.Graphics.DrawPath(Pens.Blue, gp);
                if (_featureType == FeatureType.Polygon)
                {
                    Brush fill = new SolidBrush(Color.FromArgb(70, Color.LightCyan));
                    e.Graphics.FillPath(fill, gp);
                    fill.Dispose();
                }
            }

            Pen bluePen = new Pen(Color.Blue, 2F);
            Pen redPen = new Pen(Color.Red, 3F);
            Brush redBrush = new SolidBrush(Color.Red);
            List<Point> points = new List<Point>();
            e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
            if (_coordinates != null)
            {
                points.AddRange(_coordinates.Select(coord => Map.ProjToPixel(coord)));
                foreach (Point pt in points)
                {
                    e.Graphics.FillRectangle(redBrush, new Rectangle(pt.X - 2, pt.Y - 2, 4, 4));
                }
                if (points.Count > 1)
                {
                    if (_featureType != FeatureType.MultiPoint)
                    {
                        e.Graphics.DrawLines(bluePen, points.ToArray());
                    }
                }
                if (points.Count > 0 && _standBy == false)
                {
                    if (_featureType != FeatureType.MultiPoint)
                    {
                        e.Graphics.DrawLine(redPen, points[points.Count - 1], _mousePosition);
                    }
                }
            }
            bluePen.Dispose();
            redPen.Dispose();
            redBrush.Dispose();
            base.OnDraw(e);
        }

        /// <summary>
        /// 图形的特殊显示
        /// </summary>
        /// <param name="e"></param>
        private void DisplayFeature(MapDrawArgs e)
        {
            foreach (IFeature feature in (this.InteractiveSystem.CurrentLayer as VectorLayer).LnLayer.DataSet.Features)
            {
                System.Drawing.Point pt = Map.ProjToPixel(feature.BasicGeometry.Coordinates[0]);
                e.Graphics.FillEllipse(Brushes.Black, new Rectangle(pt.X - 4, pt.Y - 4, 8, 8));
                pt = Map.ProjToPixel(feature.BasicGeometry.Coordinates[feature.BasicGeometry.Coordinates.Count - 1]);
                e.Graphics.FillEllipse(Brushes.Black, new Rectangle(pt.X - 4, pt.Y - 4, 8, 8));

                for (int i = 1; i < feature.BasicGeometry.Coordinates.Count - 1; i++)
                {
                    pt = Map.ProjToPixel(feature.BasicGeometry.Coordinates[i]);
                    e.Graphics.FillRectangle(Brushes.Black, new Rectangle(pt.X - 2, pt.Y - 2, 4, 4));
                }
            }
        }


        /// <summary>
        /// This method occurs as the mouse moves.
        /// </summary>
        /// <param name="e">The GeoMouseArcs class describes the mouse condition along with geographic coordinates.</param>
        protected override void OnMouseMove(GeoMouseArgs e)
        {
            if (!(this.InteractiveSystem.CurrentLayer is VectorLayer) || !this.InteractiveSystem.CurrentLayer.Visible) return;
            if (_standBy) return;
            if (this.InteractiveSystem.GisManager.VIEWMODE != FMAPVIEW.NORMAL) return;


            //存储临时点坐标

            this._currentCoords.X = e.GeographicLocation.X;
            this._currentCoords.Y = e.GeographicLocation.Y;



        
            if (_coordinates != null && _coordinates.Count > 0)
            {
                try
                {
                    List<Point> points = _coordinates.Select(coord => Map.ProjToPixel(coord)).ToList();
                    Rectangle oldRect = Global.GetRectangle(_mousePosition, points[points.Count - 1]);
                    Rectangle newRect = Global.GetRectangle(e.Location, points[points.Count - 1]);
                    Rectangle invalid = Rectangle.Union(newRect, oldRect);
                    invalid.Inflate(20, 20);
                    Map.Invalidate(invalid);
                }
                catch
                {
                    _coordinates.Clear();
                }
            }
            _mousePosition = e.Location;
            base.OnMouseMove(e);
        }

        /// <summary>
        /// Handles the Mouse-Up situation.
        /// </summary>
        /// <param name="e">The GeoMouseArcs class describes the mouse condition along with geographic coordinates.</param>
        protected override void OnMouseDown(GeoMouseArgs e)
        {
            Coordinate helpCoord = null;
            VectorShape helpVectorShape = null;
            IEnvelope env;
            VectorLayer vl;
            if ((vl = (this.InteractiveSystem.CurrentLayer as VectorLayer)) == null || !this.InteractiveSystem.CurrentLayer.Visible) return;
            if (_standBy) return;
            if (this.InteractiveSystem.GisManager.VIEWMODE != FMAPVIEW.NORMAL) return;
          

            // Add the current point to the featureset
            if (_featureType == FeatureType.Point)
            {
                env = this.Map.PixelToProj(new Rectangle(e.Location.X - 1, e.Location.Y - 1, 4, 4)).ToEnvelope();

                //周边点自动捕捉功能
                if (vl.PtLayer.DataSet.Features.FirstOrDefault<IFeature>(h => env.Contains(h.Coordinates[0])) != null)
                {
                    MessageBox.Show("该位置已存在点！");
                    return;
                }


                DotSpatial.Topology.Point pt = new DotSpatial.Topology.Point(this._currentCoords);

                Coordinate[] coords=new Coordinate[1];
                coords[0] = (Coordinate)this._currentCoords.Clone();

                this.InteractiveSystem.NewAskAddShape(null,coords,null,MFSHAPETYPE.POINT);

                //Feature f = new Feature(pt);
                //_featureSet.Features.Add(f);
                //_featureSet.UpdateExtent();
                //_featureSet.InvalidateVertices();


                return;
            }

            if (e.Button == MouseButtons.Right)
            {
                this.FinishShape();
            }
            else
            {
                if (_coordinates == null || _coordinates.Count == 0)
                {
                    _distance.Clear();
                    _insertCoordWithIndex.Clear();
                    _lineVertexIds.Clear();
                    _coordinates = new List<Coordinate>();
                }
                
                env = this.Map.PixelToProj(new Rectangle(e.Location.X-3,e.Location.Y-3,6,6)).ToEnvelope();

                if (_coordinates.Count > 0)
                {
                    if (_coordinates.Count == 2)
                    {
                        //过滤两点闭合的环线
                        if (env.Contains(_coordinates[0]))
                        {
                            MessageBox.Show("不可添加重复线段！");
                            this._coordinates.Clear();
                            this._lineVertexIds.Clear();
                            this.Map.Invalidate();
                            return;
                        }
                    }
                    //不可在当前点添加重复的点
                    if (env.Contains(_coordinates[_coordinates.Count - 1]))
                        return;
                }
                

                //周边点自动捕捉功能
                List<VectorShape> nodeShapes = vl.Shapes.FindAll(h => h.GetType() == typeof(NodeShape));
                List<VectorShape> lineShapes = vl.Shapes.FindAll(h => h.GetType() == typeof(LineShape));

                if ((helpVectorShape = nodeShapes.FirstOrDefault(h => env.Contains(helpCoord = h.Geometry.Coordinates[0]))) != null)
                {
                    if (helpCoord.X.Equals( double.NaN) || helpCoord.Y.Equals(double.NaN))
                        return;

                    _coordinates.Add((Coordinate)helpCoord.Clone());
                    if (_lineVertexIds.Count == 0)
                        _lineVertexIds.Add(helpVectorShape.Id);
                    else
                    {
                        if (_lineVertexIds.Count == 2)
                            _lineVertexIds[1] = helpVectorShape.Id;
                        else
                            _lineVertexIds.Add(helpVectorShape.Id);
                        this.FinishShape();
                    }
                    
                }
                else if ((helpVectorShape = lineShapes.FirstOrDefault(h => ((helpCoord = h.Geometry.Coordinates.FirstOrDefault<Coordinate>(ccc => env.Contains(ccc))) != null))) != null)
                {
                    if (helpCoord.X.Equals( double.NaN) || helpCoord.Y.Equals(double.NaN))
                        return;

                    _coordinates.Add((Coordinate)helpCoord.Clone());
                    if (_lineVertexIds.Count == 0)
                        _lineVertexIds.Add(helpVectorShape.Id);
                    else
                    {
                        if (_lineVertexIds.Count == 2)
                            _lineVertexIds[1] = helpVectorShape.Id;
                        else
                            _lineVertexIds.Add(helpVectorShape.Id);
                        this.FinishShape();
                    }
                }//未添加线上相交捕获逻辑
                    //占位
                else if (_coordinates.Count > 2 && env.Contains(_coordinates[0]))
                {
                    if (_coordinates[0].X.Equals(double.NaN)|| _coordinates[0].Y.Equals(double.NaN))
                        return;

                    _coordinates.Add(_coordinates[0]);

                    if (_insertCoordWithIndex.Count > 0)
                    {
                        _insertCoordWithIndex.Add(new List<int>(), _insertCoordWithIndex.Values.ToArray()[0]);
                    }
                    else
                    {
                        _lineVertexIds[0] = "Same";
                    }
                    this.FinishShape();
                }
                else
                {
                    bool isFind = false;
                    List<IFeature> fs = (vl.LnLayer.DataSet as FeatureSet).Features.ToList();
                    foreach (IFeature item in fs)
                    {
                        if (item.Intersects(env))
                        {

                            LineShape ls = (LineShape)vl.GetShape( item.DataRow["id"].ToString());
                            if (_coordinates.Count < 2)
                                _lineVertexIds.Add(ls.Id);
                            else
                                _lineVertexIds[1] = ls.Id;

                            int count = ls.Geometry.Coordinates.Count;
                            LineString lsg;
                            IFeature helpFeature=new Feature();
                            for (int i = 1; i < count ; i++)
                            {
                                lsg = new LineString(new List<Coordinate>() { ls.Geometry.Coordinates[i - 1], ls.Geometry.Coordinates[i] });
                                helpFeature = new Feature(lsg);
                                IBasicGeometry interSeg;
                                if ((interSeg = env.Intersection(lsg)) != null)
                                {
                                    Coordinate totalCoor = new Coordinate(0,0);
                                    for (int j = 0; j < interSeg.Coordinates.Count; j++)
                                    {
                                        totalCoor += interSeg.Coordinates[j];
                                    }

                                    Coordinate coo = totalCoor / interSeg.Coordinates.Count;
                                    if (_lineVertexIds.Count == 1)
                                    {
                                        if (coo.X.Equals( double.NaN) || coo.Y.Equals(double.NaN))
                                        {
                                            _lineVertexIds.Clear();
                                            return;
                                        }

                                        _distance.Add(coo.Distance(lsg.Coordinates[0]));

                                        _insertCoordWithIndex.Add(new List<int>() { _lineVertexIds.Count - 1, i - 1 }, coo);
                                        _coordinates.Add(coo);
                                    }
                                    else
                                    {
                                        if (coo.X.Equals(double.NaN) || coo.Y.Equals(double.NaN))
                                        {
                                            return;
                                        }

                                        if (_lineVertexIds[0] == _lineVertexIds[1] && _insertCoordWithIndex.Count > 0 && _insertCoordWithIndex.Keys.ToArray()[0][1] == i - 1)
                                        {
                                            double secendDis = coo.Distance(lsg.Coordinates[0]);
                                            if (_distance[0] == secendDis)
                                            {
                                                _insertCoordWithIndex.Add(new List<int>(), _insertCoordWithIndex.Values.ToArray()[0]);
                                            }
                                            else if (_distance[0] < secendDis)
                                            {
                                                _insertCoordWithIndex.Add(new List<int>() { _lineVertexIds.Count - 1, i - 1 }, coo);
                                            }
                                            else
                                            {
                                                _insertCoordWithIndex.Add(new List<int>() { _lineVertexIds.Count - 1, i - 1 }, coo);
                                                _insertCoordWithIndex.Reverse();
                                            }
                                        }
                                        else
                                        {
                                            _insertCoordWithIndex.Add(new List<int>() { _lineVertexIds.Count - 1, i - 1 }, coo);
                                        }
                                        _coordinates.Add(coo);
                                        this.FinishShape();
                                        return;
                                    }
                                    break;
                                }
                            }

                            isFind = true;
                            break;
                        }
                    }

                    if (!isFind)
                    {
                        helpCoord = Map.PixelToProj(e.Location);

                        if (helpCoord.X .Equals(double.NaN) || helpCoord.Y .Equals( double.NaN))
                            return;

                        if (_coordinates.Count < 2)
                            _lineVertexIds.Add("");
                        _coordinates.Add(helpCoord);
                    }
                }

                if (_coordinates.Count > 1)
                {
                    Point p1 = Map.ProjToPixel(_coordinates[_coordinates.Count - 1]);
                    Point p2 = Map.ProjToPixel(_coordinates[_coordinates.Count - 2]);
                    Rectangle invalid = Global.GetRectangle(p1, p2);
                    invalid.Inflate(20, 20);
                    Map.Invalidate(invalid);
                }
            }

            base.OnMouseDown(e);
        }

        /// <summary>
        /// Delete the shape currently being edited.
        /// </summary>
        /// <param name="sender">The sender of the DeleteShape event.</param>
        /// <param name="e">An empty EventArgument.</param>
        public void DeleteShape(object sender, EventArgs e)
        {
            if (!(this.InteractiveSystem.CurrentLayer is VectorLayer) || !this.InteractiveSystem.CurrentLayer.Visible) return;
            if (_standBy) return;
            if (this.InteractiveSystem.GisManager.VIEWMODE != FMAPVIEW.NORMAL) return;
            _coordinates = new List<Coordinate>();
            _parts = new List<List<Coordinate>>();
            Map.Invalidate();
        }

        /// <summary>
        /// Finish the shape.
        /// </summary>
        /// <param name="lineBreak">key的第一个值表示第几条线 从0开始索引， 第二个值代表第几个节点 索引从零开始</param>
        public void FinishShape()
        {
            switch (this.MfShapeType)
            {
                case MFSHAPETYPE.POINT:

                    //这个白痴事件不在这里激活.在mouseup
                    //this.InteractiveSystem.AskAddShape(_coordinates.ToArray(), MFSHAPETYPE.POINT);

                    break;
                case MFSHAPETYPE.LINE:
                    if (_coordinates == null || _coordinates.Count < 2)
                    {
                        (this.InteractiveSystem.CurrentLayer as VectorLayer).LnLayer.Invalidate();
                    }
                    else
                    {
                        this.InteractiveSystem.NewAskAddShape(null, _coordinates.ToArray(), _lineVertexIds, MFSHAPETYPE.LINE, (_insertCoordWithIndex == null || _insertCoordWithIndex.Count < 1) ? null : _insertCoordWithIndex);
                    }
                    break;



                default:
                    break;
            }
            if (_distance != null)
                _distance.Clear();
            if (_insertCoordWithIndex != null)
                _insertCoordWithIndex.Clear();
            if (_coordinates != null)
                _coordinates.Clear();
            if (_lineVertexIds != null)
                _lineVertexIds.Clear();
            if (_parts != null)
                _parts.Clear();
        }

        /// <summary>
        /// Finish the part of the shape being edited.
        /// </summary>
        /// <param name="sender">The object sender.</param>
        /// <param name="e">An empty EventArgs class.</param>
        public void FinishPart()
        {
            _parts.Add(_coordinates);
            _coordinates = new List<Coordinate>();
            Map.Invalidate();
        }

        /// <summary>
        /// Occurs when this function is removed.
        /// </summary>
        protected override void OnUnload()
        {
            if (Enabled)
            {
                _coordinates = null;
                
            }
            if (_tempLayer != null)
            {
                Map.MapFrame.DrawingLayers.Remove(_tempLayer);
                Map.MapFrame.Invalidate();

                _tempLayer = null;
            }
            Map.Invalidate();
        }

        #endregion

        #region Properties


        //付加


        /// <summary>
        /// 要添加的图形类型
        /// </summary>
        public MFSHAPETYPE MfShapeType
        {
            get { return _mfShapeType; }
            set { _mfShapeType = value;

            switch (this._mfShapeType)
            {
                case MFSHAPETYPE.POINT:
                    this._featureType = FeatureType.Point;
                    break;
                case MFSHAPETYPE.LINE:
                    this._featureType = FeatureType.Line;

                    break;
                //case MFSHAPETYPE.POLYLINE:

                //    this._featureType = FeatureType.Line;

                //    break;
                case MFSHAPETYPE.POLYGON:

                    this._featureType = FeatureType.Polygon;

                    break;
                default:
                    break;
            }
            
            }
        }





        /// <summary>
        /// Gets or sets the featureset to modify.
        /// </summary>
        //public IFeatureSet FeatureSet
        //{
        //    get { return _featureSet; }
        //    set { _featureSet = value; }
        //}

        #endregion

        private void CoordinateDialogFormClosing(object sender, FormClosingEventArgs e)
        {
            // This signals that we are done with editing, and should therefore close up shop
            Enabled = false;
        }

        /// <summary>
        /// Actually, this creates disposable items but doesn't own them.
        /// When the ribbon disposes it will remove the items.
        /// </summary>
        public void Dispose()
        {
            Dispose(true);

            // This exists to prevent FX Cop from complaining.
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// Finalizes an instance of the AddShapeFunction class.
        /// </summary>
        ~HAddLineMF()
        {
            Dispose(false);
        }

        /// <summary>
        /// Gets a value indicating whether the "dispose" method has been called.
        /// </summary>
        public bool IsDisposed
        {
            get { return _disposed; }
        }

        /// <summary>
        /// Disposes this handler, removing any buttons that it is responsible for adding.
        /// </summary>
        /// <param name="disposeManagedResources">Disposes of the resources.</param>
        protected virtual void Dispose(bool disposeManagedResources)
        {
            if (!_disposed)
            {
                // One option would be to leave the non-working tools,
                // but if this gets disposed we should clean up after
                // ourselves and remove any added controls.
                if (disposeManagedResources)
                {
                   
                    
                  
                  
                    _coordinates = null;
                  
                    _tempLayer = null;
                   
                   
                    _parts = null;
                }
                _disposed = true;
            }
        }


    }



}
